﻿using System;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using Telerik.Windows.Controls;
using System.Collections.Generic;
using System.ServiceModel.DomainServices.Client;
using TotalSystems.Core.Infrastructure;
using System.Collections;
using System.Threading;
using System.Windows.Data;

namespace TotalSystems.Core.Infrastructure
{
    public class BaseDefenitionTreeViewBuilder<T> where T:Entity
    {
        public string IDPropertyName { get; set; }

        public string NamePropertyName { get; set; }

        public string ParentIdPropertyName { get; set; }

        public string IsExpandedPropertyName { get; set; }

        public string IsActivePropertyName { get; set; }

        public IEnumerable<T> Entities { get; set; }

        public NodeMode NodeMode { get; set; }

        public ObservableCollection<RadTreeViewItem> BuildTreeView(IEnumerable<T> entities,NodeMode nodeMode = NodeMode.Active)
        {
            ObservableCollection<RadTreeViewItem> treeViewCol = new ObservableCollection<RadTreeViewItem>();
            var rootNodes = entities.Where(p => (decimal)p.GetPropertyValue(ParentIdPropertyName) == -1);
            foreach (var rootNode in rootNodes)
            {
                RadTreeViewItem rtvi = new RadTreeViewItem();
                rtvi.Header = rootNodes.GetPropertyValue(NamePropertyName);
                rtvi.Tag = rootNode;
                rtvi.IsExpanded = (bool)rootNode.GetPropertyValue(IsExpandedPropertyName);
            }
            return treeViewCol;
        }

        public ObservableCollection<RadTreeViewItem> BuildTreeView(IEnumerable<T> entities,string idPropertyName,
            string namePropertyName,string parentIdPropertyName,string isExpandedPropertyName,string isActivePropertyName,NodeMode nodeMode = NodeMode.Active)
        {
            Entities = entities;
            NodeMode = nodeMode;
            IDPropertyName=idPropertyName;
            NamePropertyName= namePropertyName;
            ParentIdPropertyName = parentIdPropertyName;
            IsExpandedPropertyName = isExpandedPropertyName;
            IsActivePropertyName = isActivePropertyName;

            ObservableCollection<RadTreeViewItem> treeViewCol = new ObservableCollection<RadTreeViewItem>();
            var rootNodes = new List<T>();
            switch (nodeMode)
            {
                case NodeMode.Active:
                    rootNodes = Entities.Where(x => x.GetDecimalValue(ParentIdPropertyName) == -1 && (bool?)x.GetPropertyValue(IsActivePropertyName) == true).ToList();
                    break;
                case NodeMode.Inactive:
                    rootNodes = Entities.Where(x => x.GetDecimalValue(ParentIdPropertyName) == -1 ).ToList();
                    break;
                case NodeMode.Both:
                    rootNodes = Entities.Where(x => x.GetDecimalValue(ParentIdPropertyName) ==-1).ToList();
                    break;
            }
            rootNodes = Entities.Where(x => x.GetDecimalValue(ParentIdPropertyName) == -1).ToList();
            foreach (var rootNode in rootNodes)
            {
                Binding tvNodeTagBinding = new Binding { RelativeSource = new RelativeSource(RelativeSourceMode.Self), Path = new PropertyPath("Tag." + namePropertyName) };
                RadTreeViewItem rtvi = new RadTreeViewItem();
                //rtvi.Header = rootNode.GetPropertyValue(namePropertyName);
                rtvi.SetBinding(RadTreeViewItem.HeaderProperty, tvNodeTagBinding);
                rtvi.Tag = rootNode;
                rtvi.IsExpanded = (bool)rootNode.GetPropertyValue(isExpandedPropertyName);
                BuildChildren(rootNode, rtvi, nodeMode);
                treeViewCol.Add(rtvi);
            }
            if (NodeMode == Infrastructure.NodeMode.Inactive && treeViewCol.Count >0)
                FilterActiveNodes(treeViewCol.First());
            return treeViewCol;
        }

        public List<T> RemoveItemsFromTreeView(ObservableCollection<RadTreeViewItem> treeViewCollection, IList items, IEnumerable<T> oldItems)
        {
            List<T> removedItems = new List<T>();
            foreach (var item in oldItems)
            {
                var oldNode = FindChidNode<T>(treeViewCollection, item.GetDecimalValue(IDPropertyName));
                //if (oldNode != null)
                //{
                //    for (int i = 0; i < oldNode.Items.Count; i++)
                //    {
                //        var childItem = items.Cast<T>().First<T>(p => p.GetDecimalValue(IDPropertyName) == ((T)((RadTreeViewItem)oldNode.Items[i]).Tag).GetDecimalValue(IDPropertyName));
                //        //var obItems = items as ObservableCollection<T>;
                //        items.Remove(childItem);
                //        i--;
                //    }
                //    var parentNode = oldNode.Parent as RadTreeViewItem;
                //    if (parentNode != null)
                //        parentNode.Items.Remove(oldNode);
                //}
                RemoveNode2<T>(oldNode,ref removedItems);
                var parnetNode = oldNode.Parent;
                if (parnetNode == null)
                {
                    treeViewCollection.Remove(oldNode);
                }
            }
            if (NodeMode == Infrastructure.NodeMode.Active)
            {
                var tmp = items.Cast<T>().Where(p => (bool?)p.GetPropertyValue(IsActivePropertyName) != true && removedItems.Select(x => x.GetDecimalValue(IDPropertyName)).Contains(p.GetDecimalValue(ParentIdPropertyName)));
                removedItems.AddRange(tmp);
            }
            
            return removedItems;
        }

        private void RemoveNode2<T>(RadTreeViewItem node, ref List<T> removedItems)
        {
            removedItems.Add((T)node.Tag);
            for (int i = 0; i < node.Items.Count; i++)
            {
                RemoveNode2<T>((RadTreeViewItem)node.Items[i], ref removedItems);
                --i;
            }
            var parentNode = node.Parent as RadTreeViewItem;
            if (parentNode != null)
            {
                parentNode.Items.Remove(node);
            }
        }

        private void RemoveNode<T>(IEnumerable<RadTreeViewItem> tvCol, decimal id,ref List<T> removedItems)
        {
            var node = FindChidNode<T>(tvCol, id);
            if (node != null)
            {
                removedItems.Add((T)node.Tag);
                for (int i = 0; i < node.Items.Count; i++)
                {
                    RemoveNode<T>(node.Items.Cast<RadTreeViewItem>(), ((T)((RadTreeViewItem)node.Items[i]).Tag).GetDecimalValue(IDPropertyName), ref removedItems);
                }
                var parentNode = node.Parent as RadTreeViewItem;
                if (parentNode != null)
                {
                    parentNode.Items.Remove(node);
                }
            }
        }

        public List<RadTreeViewItem> AddItemsToTreeView(ObservableCollection<RadTreeViewItem> treeViewCollection, IEnumerable<T> items, IEnumerable<T> newItems)
        {
            var rootNodes = new List<T>();
            var addedNodes = new List<RadTreeViewItem>();

            switch (NodeMode)
            {
                case NodeMode.Active:
                    rootNodes = newItems.Where(x => x.GetDecimalValue(ParentIdPropertyName) == -1 && (bool?)x.GetPropertyValue(IsActivePropertyName) == true).ToList();
                    break;
                case NodeMode.Inactive:
                    rootNodes = newItems.Where(x => x.GetDecimalValue(ParentIdPropertyName) == -1 && (bool?)x.GetPropertyValue(IsActivePropertyName) != true).ToList();
                    break;
                case NodeMode.Both:
                    rootNodes = newItems.Where(x => x.GetDecimalValue(ParentIdPropertyName) == -1).ToList();
                    break;
            }

            var childNodes = new List<T>();
            switch (NodeMode)
            {
                case NodeMode.Active:
                    childNodes = newItems.Where(p => !rootNodes.Contains(p) && (bool?)p.GetPropertyValue(IsActivePropertyName) == true).ToList();
                    break;
                case NodeMode.Inactive:
                    childNodes = newItems.Where(p => !rootNodes.Contains(p) && (bool?)p.GetPropertyValue(IsActivePropertyName) == false).ToList();
                    break;
                case NodeMode.Both:
                    childNodes = newItems.Where(p => !rootNodes.Contains(p)).ToList();
                    break;
            }

            foreach (var rootNode in rootNodes)
            {
                Binding tvNodeTagBinding = new Binding { RelativeSource = new RelativeSource(RelativeSourceMode.Self), Path = new PropertyPath("Tag." + NamePropertyName) };
                RadTreeViewItem rtvi = new RadTreeViewItem();
                addedNodes.Add(rtvi);
                //rtvi.Header = rootNode.GetPropertyValue(NamePropertyName);
                rtvi.SetBinding(RadTreeViewItem.HeaderProperty, tvNodeTagBinding);
                rtvi.Tag = rootNode;
                rtvi.IsExpanded = (bool)rootNode.GetPropertyValue(IsExpandedPropertyName);
                treeViewCollection.Add(rtvi);
                //AddChildern(rootNode, childNodes, rtvi);
            }
            AddChildern(items, childNodes, treeViewCollection,ref addedNodes);
            //switch (NodeMode)
            //{
            //    case NodeMode.Active:
            //        rootNodes = items.Where(x => x.GetDecimalValue(ParentIdPropertyName) == -1 && (bool?)x.GetPropertyValue(IsActivePropertyName) == true).ToList();
            //        break;
            //    case NodeMode.NotActive:
            //        rootNodes = items.Where(x => x.GetDecimalValue(ParentIdPropertyName) == -1 && (bool?)x.GetPropertyValue(IsActivePropertyName) == false).ToList();
            //        break;
            //    case NodeMode.Both:
            //        rootNodes = items.Where(x => x.GetDecimalValue(ParentIdPropertyName) == -1).ToList();
            //        break;
            //}

            //foreach (var rootNode in rootNodes)
            //{
            //    AddChildern(rootNode, childNodes, treeViewCollection.First(p => ((T)p.Tag).GetDecimalValue(IDPropertyName) == rootNode.GetDecimalValue(IDPropertyName)));
            //}
            return addedNodes;
        }

        private void AddChildern(IEnumerable<T> entitis, IEnumerable<T> newChildren, IEnumerable<RadTreeViewItem> tvCollection,ref List<RadTreeViewItem> AddedTVCollection)
        {
            var remainedChildern = new List<T>(newChildren);
            var children = new List<T>();
            children.Clear();
            // find all the children of the item 
            foreach (var child in newChildren)
            {
                var parentItem = entitis.FirstOrDefault(p => p.GetDecimalValue(IDPropertyName) == child.GetDecimalValue(ParentIdPropertyName));
                if (parentItem != null)
                {
                    Binding tvNodeTagBinding = new Binding { RelativeSource = new RelativeSource(RelativeSourceMode.Self), Path = new PropertyPath("Tag." + NamePropertyName) };
                    RadTreeViewItem chrtvi = new RadTreeViewItem();
                    AddedTVCollection.Add(chrtvi);
                    remainedChildern.Remove(child);
                    chrtvi.Tag = child;
                    //chrtvi.Header = child.GetPropertyValue(NamePropertyName);
                    chrtvi.SetBinding(RadTreeViewItem.HeaderProperty, tvNodeTagBinding);
                    if ((bool?)child.GetPropertyValue(IsExpandedPropertyName) == true) { chrtvi.IsExpanded = true; }
                    var parentNode = FindChidNode<T>(tvCollection, parentItem.GetDecimalValue(IDPropertyName));
                    parentNode.Items.Add(chrtvi);
                }
            }
            if (remainedChildern.Count != newChildren.Count() && remainedChildern.Count != 0)
                AddChildern(entitis, remainedChildern, tvCollection,ref AddedTVCollection);

            //foreach (var child in children)
            //{
            //    //var parentNode = treeViewCollection.FirstOrDefault(p => ((T)p.Tag).GetDecimalValue(IDPropertyName) == child.GetDecimalValue(ParentIdPropertyName));

            //        RadTreeViewItem chrtvi = new RadTreeViewItem();
            //        chrtvi.Header = child.GetPropertyValue(NamePropertyName);
            //        chrtvi.Tag = child;
            //        if ((bool?)child.GetPropertyValue(IsExpandedPropertyName) == true) { chrtvi.IsExpanded = true; }

            //        tvItem.Items.Add(chrtvi);
            //        AddChildern(child, children, chrtvi);

            //}

        }

        public RadTreeViewItem FindChidNode<T>(IEnumerable<RadTreeViewItem> tvCol, decimal id)
        {
            RadTreeViewItem foundNode = null;
            foreach (var node in tvCol)
            {
                if (((T)node.Tag).GetDecimalValue(IDPropertyName) == id)
                    return node;
                else
                    foundNode = FindChidNode<T>(node.Items.Cast<RadTreeViewItem>(), id);
                if(foundNode != null)
                    return foundNode;
            }
            return foundNode;
        }

        public void FilterActiveNodes(RadTreeViewItem node)
        {
            List<RadTreeViewItem> toRemoveNodes = new List<RadTreeViewItem>();
            foreach (RadTreeViewItem item in node.Items)
            {
                if ((bool?)item.Tag.GetPropertyValue(IsActivePropertyName) == true)
                    toRemoveNodes.Add(item);
            }
            foreach (var item in toRemoveNodes)
            {
                var tmpChildern = new List<object>(item.Items);

                
                item.Items.Clear();
                foreach (var child in tmpChildern)
                {
                    ((RadTreeViewItem)item.Parent).Items.Add(child);
                }
                node.Items.Remove(item);
                //foreach (var child in tmpChildern)
                //{
                //    FilterActiveNodes(child as RadTreeViewItem);
                //}
                if (node.Items.Where(p => (bool?)((RadTreeViewItem)p).Tag.GetPropertyValue(IsActivePropertyName) == true).Any())
                    FilterActiveNodes(node);
            }
        }

        private void BuildChildren(T entity,RadTreeViewItem tvItem,NodeMode nodeMode)
        {
            var children = new List<T>();
            children.Clear();
            // find all the children of the item 
            switch (nodeMode)
            {
                case NodeMode.Active:
                    children = Entities.Where(x => x.GetDecimalValue<T>(ParentIdPropertyName)  == entity.GetDecimalValue(IDPropertyName) && x.GetDecimalValue(IDPropertyName) != entity.GetDecimalValue(IDPropertyName) && (bool?)x.GetPropertyValue(IsActivePropertyName) == true).ToList();
                    break;
                case NodeMode.Inactive:
                    children = Entities.Where(x => x.GetDecimalValue<T>(ParentIdPropertyName) == entity.GetDecimalValue(IDPropertyName) && x.GetDecimalValue(IDPropertyName) != entity.GetDecimalValue(IDPropertyName)).ToList();
                    break;
                case NodeMode.Both:
                    children = Entities.Where(x => x.GetDecimalValue<T>(ParentIdPropertyName) == entity.GetDecimalValue(IDPropertyName) && x.GetDecimalValue(IDPropertyName) != entity.GetDecimalValue(IDPropertyName)).ToList();
                    break;
            }
            //children = Entities.Where(x => x.GetDecimalValue<T>(ParentIdPropertyName) == entity.GetDecimalValue(IDPropertyName) && x.GetDecimalValue(IDPropertyName) != entity.GetDecimalValue(IDPropertyName)).ToList();
            // add the child to the item's children collection and call the FindChildren recursively, in case the child has children
            foreach (T child in children)
            {
                Binding tvNodeTagBinding = new Binding { RelativeSource = new RelativeSource(RelativeSourceMode.Self), Path = new PropertyPath("Tag." + NamePropertyName) };
                RadTreeViewItem chrtvi = new RadTreeViewItem();
                //chrtvi.Header = child.GetPropertyValue(NamePropertyName);
                chrtvi.SetBinding(RadTreeViewItem.HeaderProperty, tvNodeTagBinding);
                chrtvi.Tag = child;
                if ((bool?)child.GetPropertyValue(IsExpandedPropertyName) == true) { chrtvi.IsExpanded = true; }

                tvItem.Items.Add(chrtvi);
                BuildChildren(child, chrtvi,nodeMode);
            }
        }
    
        
    }
}
